package re_0905.question_meituan;

import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author moter-code01
 */
public class MoterThreadPoolExecutor {
    private Lock submitLock = new ReentrantLock();
    //    private Lock[] tasks;
//    private Condition[] conditions;
    private Worker[] workers;
    private volatile AtomicInteger state;//线程池状态
    private volatile AtomicInteger workCount;//任务数量
    //线程池状态
    private final int RUNNING = 0;//可接受任务状态
    private final int STOP = 1;//不接受任务，不出处理任务，中断所有处理的任务
    private final int SHUTDOWN = 2;//关闭状态，不接受新的任务，继续处理剩余的任务
    private final int TIDYING = 3;//所有任务都处理完成
    private final int TERMINATED = 4;//线程池最终关闭
    private AtomicInteger num;//线程编号
    //
    private int coreSize;//核心线程数
    //    private int maxSize;//最大线程数
    private BlockingQueue<Runnable> queue;//阻塞队列
    private Boolean rejectHandler;//拒绝策略 true不处理，false抛异常

    public MoterThreadPoolExecutor(int coreSize, BlockingQueue<Runnable> queue) throws Exception {
        this(coreSize, queue, false);
    }

    public MoterThreadPoolExecutor(int coreSize, BlockingQueue<Runnable> queue, Boolean rejectHandler) throws Exception {
        if (coreSize <= 0) {
            throw new Exception("线程池大小不能为0");
        }
        Objects.requireNonNull(queue, "队列不能为null");
        Objects.requireNonNull(rejectHandler, "拒绝策略不能为空");


        this.coreSize = coreSize;
        this.queue = queue;
        this.rejectHandler = rejectHandler;
        init();
    }

    /**
     * 初始化设置线程池状态和任务数量
     */
    private void init() {
        //初始化线程池状态
        state = new AtomicInteger(0);
        workCount = new AtomicInteger(0);//初始化任务数量。
        num = new AtomicInteger(0);//这个用于，给每个外卖员分配第一个任务
        //这里我的实现细节是，每个外卖员通过submit拿到自己的第一个任务，然后就不从submit拿去任务了，自动去阻塞队列拿去任务。

        workers = new Worker[coreSize];//初始化外卖员数组
        stopAll();//注释在下面
    }

    /**
     * 这里定义了以线程，用来不断判断 是否终止，就是模拟线程池的shutdownNow方法。停止所有线程工作，全部中断
     * 但是实际情况执行的时候，这个线程好像抢不到资源，没有办法执行，一直再阻塞，暂时不知道怎么解决。
     */
    public void stopAll() {
        Thread stop = new Thread(() -> {
            while (true) {
                if (state.get() == STOP) {
                    for (Worker worker : workers) {
                        worker.interrupt();
                    }
                    break;
                }
            }
        });
        stop.setDaemon(true);//设置为守护线程
        stop.start();
    }

    /**
     * 提交任务
     *
     * @param task
     * @throws Exception
     */
    public void submit(Runnable task) throws Exception {
        Objects.requireNonNull(task, "任务为空");
        submitLock.lock();
        try {
            //如果线程不能接受新任务抛出异常
            if (state.get() != RUNNING) {
                throw new Exception("无法接受新任务！");
            }
            //如果核心线程有空余,且核心线程有空余线程没有启动起来
            else if (workCount.get() < coreSize && num.get() < coreSize) {
                //启动一个线程，
                addTask(task);
            }
            //上面条件不满足，就加入队列
            else if (queue.remainingCapacity() > 0) {
                //加入队列
                queue.put(task);
            } else {
                reject();//拒绝
            }
        } finally {
            submitLock.unlock();
        }
    }

    /**
     * 拒绝策略
     * false：就是抛异常，true：不管这个任务
     *
     * @throws Exception
     */
    private void reject() throws Exception {
        if (!rejectHandler) {
            throw new Exception("订单满了，请稍后添加！");
        }
    }

    /**
     * 每个外卖员的第一次任务
     * 包含外卖员的初始化，线程开始
     * num自增使用来找到下一个外卖员，num达到coreSize就不会再进入这个函数了。 父方法有条件判断
     * @param task
     */
    private void addTask(Runnable task) {
        workers[num.get()] = new Worker(task, num.get());
        System.out.println("外卖员" + workers[num.get()].number + "\t接单");
        new Thread(workers[num.get()]).start();
        num.getAndIncrement();
    }

    /**
     * worker内部类代表外卖员
     */
    class Worker extends Thread {
        int number;//编号
        Runnable firstTask;

        public Worker(Runnable task, int i) {
            firstTask = task;
            this.number = i;
        }

        @Override
        public void run() {
            try {
                runWorker(this);//外卖员工作细节
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 外卖员线程的run方法。
     *
     * @param r
     * @throws InterruptedException
     */
    public void runWorker(Worker r) throws InterruptedException {
        Runnable w = r.firstTask;

        while (true) {
            while (w != null || (w = getTask()) != null) {//不断去队列拿取任务来执行
                w.run();
                System.out.println("外卖员" + r.number + "\t订单送达");
                w = null;
                workCount.decrementAndGet();
            }
            //如果线程池状态时中断  或者 是shutdown且队列没有任务，当前线程就执行完毕，推出。
            if (state.get() == STOP || (state.get() == SHUTDOWN && queue.size() == 0)) {
                break;
            }
            System.out.println(queue.size());
//            Thread.yield();//礼让
        }
    }

    /**
     * 从队列获取任务
     *
     * @return
     * @throws InterruptedException
     */
    public Runnable getTask() throws InterruptedException {
        return queue.poll(2, TimeUnit.SECONDS);
    }


    private void setStop() {
        state.set(STOP);
    }

    public void shutDown() {
        state.set(SHUTDOWN);
    }

    public void shutDownNow() {
        setStop();
    }
}
